package com.jiangwei.flv.service.impl;


import com.alibaba.fastjson.util.IOUtils;
import com.google.common.collect.Lists;
import com.jiangwei.flv.config.StreamConfig;
import com.jiangwei.flv.factories.Converter;
import com.jiangwei.flv.factories.ConverterFactories;
import com.jiangwei.flv.factories.ConverterTranFactories;
import com.jiangwei.flv.service.IFLVService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.AsyncContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * @author jiangwei97@aliyun.com
 * @since 2023/12/11 9:42
 */
@Slf4j
@Service
public class FLVService implements IFLVService, DisposableBean {

    private static final ConcurrentHashMap<String, Converter> converters = new ConcurrentHashMap<>();

    private static final ThreadPoolExecutor executor;

    static {
        executor = new ThreadPoolExecutor(
                8,
                64,
                60,
                TimeUnit.SECONDS,
                new SynchronousQueue<>(),
                new ThreadPoolExecutor.AbortPolicy()
        );
    }

    @Resource
    private StreamConfig streamConfig;

    @Override
    public synchronized void open(String key, HttpServletResponse response, HttpServletRequest request) {
//        String key = md5(url);
        if(!streamConfig.resource.containsKey(key)){
            log.error("没有找到对应的视频源");
            try {
                PrintWriter writer = response.getWriter();
                writer.println("没有找到对应的视频源");
                writer.flush();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            return;
        }
        String url = streamConfig.getResource().get(key);

        AsyncContext async = request.startAsync();
        async.setTimeout(0);
        if (converters.containsKey(key)) {
            Converter c = converters.get(key);
            try {
                c.addOutputStreamEntity(key, async);
            } catch (IOException e) {
                log.error(e.getMessage(), e);
                throw new IllegalArgumentException(e.getMessage());
            }
        } else {
            List<AsyncContext> outs = Lists.newArrayList();
            outs.add(async);

//            String url = "rtsp://admin:admin12345@172.16.12.112:554/h264/ch1/sub/av_stream";
            FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(url);
            if ("rtsp".equals(url.substring(0, 4))) {
                grabber.setOption("rtsp_transport", "tcp");
                grabber.setOption("stimeout", "3000");
            }
            try {
                grabber.start();
            } catch (FFmpegFrameGrabber.Exception e) {
                throw new RuntimeException(e.getMessage());
            }
            // 来源视频H264格式,音频AAC格式
            // 无须转码，更低的资源消耗，更低的延迟
            // 173  1 65543
            if (avcodec.AV_CODEC_ID_H264 == grabber.getVideoCodec()
                    && (grabber.getAudioChannels() == 0 || avcodec.AV_CODEC_ID_AAC == grabber.getAudioCodec())) {
                ConverterFactories c = new ConverterFactories(url, key, converters, outs, grabber);
                try {
                    executor.submit(c);
                    converters.put(key, c);
                } catch (RejectedExecutionException e) {
                    System.out.println("暂无可用线程");
                }
            } else {
                // 需要转码为视频H264格式,音频AAC格式
                log.info("VideoCodec: " + grabber.getVideoCodec());
                log.info("AudioCodec: " + grabber.getAudioCodec());
                ConverterTranFactories c = new ConverterTranFactories(url, key, converters, outs, grabber);
                try {
                    executor.submit(c);
                    converters.put(key, c);
                } catch (RejectedExecutionException e) {
                    System.out.println("暂无可用线程");
                }
            }
        }
        response.setContentType("video/x-flv");
        response.setHeader("Connection", "keep-alive");
        response.setStatus(HttpServletResponse.SC_OK);
        try {
            response.flushBuffer();
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
    }

    public String md5(String plainText) {
        StringBuilder buf = new StringBuilder();
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(plainText.getBytes());
            byte[] b = md.digest();
            int i;
            for (byte value : b) {
                i = value;
                if (i < 0)
                    i += 256;
                if (i < 16)
                    buf.append("0");
                buf.append(Integer.toHexString(i));
            }
        } catch (NoSuchAlgorithmException e) {
            log.error(e.getMessage(), e);
        }
        return buf.toString();
    }

    @Override
    public void destroy() {
        executor.shutdown();
    }

}
